package com.tyfspring.context.event;

import com.tyfspring.beans.aware.BeanFactoryAware;
import com.tyfspring.beans.exception.BeansException;
import com.tyfspring.beans.factory.BeanFactory;
import com.tyfspring.context.ApplicationEvent;
import com.tyfspring.context.ApplicationListener;
import com.tyfspring.util.ClassUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

/**
 * @Description 对事件广播器的公用方法提取
 * 实现一些基本方法+对getApplicationListeners摘取符合广播事件中的监听器+supportsEvent过滤动作
 * @Author shallow
 * @Date 2023/4/14 16:58
 */

public abstract class AbstractApplicationEventMuticaster implements ApplicationEventMuticaster, BeanFactoryAware {

    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event){
        LinkedList<ApplicationListener> allListeners = new LinkedList<>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
            if (supportEvent(listener,event)){
                allListeners.add(listener);
            }
        }
        return allListeners;
    }

    /**
     * 主要包括对 Cglib、Simple 不同实例化需要获取目标
     * Class，Cglib 代理类需要获取父类的 Class，普通实例化的不需要。接下来就是通过
     * 提取接口和对应的 ParameterizedType 和 eventClassName
     * @param applicationListener
     * @param event
     * @return
     */
    protected boolean supportEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass)?listenerClass.getSuperclass():listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];
        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
            eventClassName = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeansException("wrong event class name: " + className);
        }
        return eventClassName.isAssignableFrom(event.getClass());
    }
}
